﻿var http = require('http');
var zlib = require('zlib');
var URL = require('url');
var qs = require('querystring');

function httpPost(url, data) {
    return new Promise(function (onSuccess, onFailed) {
        var headers;
        var content = qs.stringify(data);
        headers = URL.parse(url);
        headers.method = "POST";
        headers.headers = {
            "accept-encoding": "gzip, deflate",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "Content-Length": content.length
        };
        
        var req = http.request(headers, function (response) {
            var data = [], encoding = response.headers['content-encoding'];
            if (response.statusCode != 200) {
                onFailed(response.statusCode);
                return;
            }
            
            response.on('data', function (chunk) {
                data.push(chunk);
            });
            
            response.on('end', function () {
                var buffer = Buffer.concat(data);
                if (encoding == 'gzip') {
                    zlib.gunzip(buffer, function (error, rawData) {
                        onSuccess(rawData.toString());
                    });
                } else if (encoding == 'deflate') {
                    zlib.inflate(buffer, function (error, rawData) {
                        onSuccess(rawData.toString());
                    });
                } else
                    onSuccess(buffer.toString());
            });
        }).on('error', function (e) {
            onFailed(e);
        }).write(content);

    });
}

function PeriodTask(id, url, period, prevTime, key) {
    this.id = id;
    this.url = url;
    this.period = period;
    this.prevTime = prevTime;
    this.key = key;
    this.status = 'key';
    
    console.log('Peroid Task [' + id + '] has been queued.');

    this.execute = function () {

        (function (task) {
            var pr = httpPost(task.url, {"access-key": task.key});
            pr.then(function (data) {
                var result;
                try {
                    result = JSON.parse(data);
                    if (result.code) {
                        console.log((new Date()).getStdFormat() + ' - Successfully executed task [' + task.id + '].');
                        task.prevTime = (new Date()).getUnixStamp();
                        task.start();
                    } else {
                        console.log((new Date()).getStdFormat() + ' - Failed to execute task [' + task.id + '], It will be retried in 5 seconds.');
                        task.prevTime = (new Date()).getUnixStamp();
                        task.start(5000);
                    }
                } catch (e) {
                    console.log((new Date()).getStdFormat() + ' - Failed to execute task [' + task.id + '], It will be retried in 5 seconds.');
                    task.prevTime = (new Date()).getUnixStamp();
                    task.start(5000);
                }

            }, function (err) {
                console.log((new Date()).getStdFormat() + ' - Failed to execute task [' + task.id + '], It will be retried in 5 seconds.');
                console.log(err);
                task.prevTime = (new Date()).getUnixStamp();
                task.start(5000);
            });
        })(this);
    };

    this.getNextTime = function () {
        var now = (new Date()).getUnixStamp();
        if (now - this.period > this.prevTime) {
            return 0;
        }
        return (this.prevTime + this.period - now) * 1000;
    };

    this.start = function (t) {
        (function (task) {
            setTimeout(function() {
                task.execute();
            }, t?t:task.getNextTime());
        })(this);
    };
}

function JScheduler(cfg) {
    var _cfg = cfg;
    var _tasks = {};

    var _status = 'ready';
    
    this.addTask = function (task) {
        if (_tasks[task.id]) {
            return;
        }
        if (task.method == 'period') {
            _tasks[task.id] = new PeriodTask(task.id, _cfg['url-base'].replace('$ACTION$', task.id), parseInt(task.expr), parseInt(task.prev_time), _cfg.key);
        }
    };

    this.getStatus = function () {
        return _status;
    };

    this.start = function () {
        for (var id in _tasks) {
            _tasks[id].start();
        }
        _status = 'working';
    };

    this.loadTasks = function (callback) {
        (function (jsc) {
            var pr = httpPost(_cfg['task-list'], {"access-key": _cfg.key});
            pr.then(function (data) {
                var taskList;
                try {
                    taskList = JSON.parse(data);
                    taskList.data.every(function (task) {
                        jsc.addTask(task);
                        return true;
                    });
                    callback();
                } catch (e) {
                    console.log('Failed to get tasklist from remote server.');
                }

            }, function (err) {
                console.log('Failed to get tasklist from remote server.');
                console.log(err);
            });
        })(this);
    };

}

module.exports = JScheduler;
